home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
Chip 2000 October
/
CHIP Turkiye Ekim 2000.iso
/
prog
/
naps
/
04
/
setup.exe
/
Gnucleus
/
GnuTransfer.cpp
< prev
next >
Wrap
C/C++ Source or Header
|
2000-07-15
|
14KB
|
595 lines
/********************************************************************************
Gnucleus - A node application for the Gnutella network
Copyright (C) 2000 John Marshall
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
For support, questions, comments, etc...
E-Mail:
swabby@c0re.net
Address:
21 Cadogan Way
Nashua, NH, USA 03062
********************************************************************************/
// GnuTransfer.cpp : implementation file
//
#include "stdafx.h"
#include "Gnucleus.h"
#include "GnucleusDoc.h"
#include "ViewSearch.h"
#include "ViewTransfer.h"
#include "GnuHash.h"
#include "GnuControl.h"
#include "GnuTransfer.h"
#include <process.h>
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
IMPLEMENT_DYNCREATE(CGnuTransfer, CAsyncSocket)
/////////////////////////////////////////////////////////////////////////////
// CGnuTransfer
CGnuTransfer::CGnuTransfer()
{
Initialize();
IsFileOpen = 0;
}
CGnuTransfer::CGnuTransfer(CGnuControl *pComm)
{
Initialize();
GnuComm = pComm;
}
CGnuTransfer::Initialize()
{
GnuComm = NULL;
next = NULL;
IsFileOpen = 0;
IsTransfering = 0;
BytesCompleted = 0;
OldBytesCompleted = 0;
OldRate = 0;
m_dwBytesIn = m_dwBytesOut = m_dwTotalBytesIn = m_dwTotalBytesOut
= m_dwByteAllottmentOut = m_dwByteAllottmentIn = 0;
AllocBw = -1;
m_threadHandle = NULL;
m_timeLastHeardAT = CTime::GetCurrentTime();
}
CGnuTransfer::~CGnuTransfer()
{
IsTransfering = 0;
if(m_threadHandle)
{
switch (::WaitForSingleObject (m_threadHandle, 10000))
{
case WAIT_OBJECT_0: // Normal, the thread exited properly
break;
case WAIT_TIMEOUT: // Thread didn't exit, kill it
::TerminateThread (m_threadHandle, 0);
break;
case WAIT_FAILED: // Thread probably exited before we got to the waitforsingleobject
break;
default: // err
::TerminateThread (m_threadHandle, 0);
break;
}
}
}
// Do not edit the following lines, which are needed by ClassWizard.
#if 0
BEGIN_MESSAGE_MAP(CGnuTransfer, CAsyncSocket)
//{{AFX_MSG_MAP(CGnuTransfer)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
#endif // 0
/////////////////////////////////////////////////////////////////////////////
// CGnuTransfer member functions
void CGnuTransfer::OnConnect(int nErrorCode)
{
if(nErrorCode == 0)
{
if(Type == 'D')
{
Status = "Remotely queued";
CString GetCommand = "GET /get/";
GetCommand += DWrdtoStr(FileInfo.Index);
GetCommand += "/";
GetCommand += FileInfo.FileName;
GetCommand += " HTTP/1.0\r\nConnection: Keep-Alive\r\nRange: bytes=";
GetCommand += DWrdtoStr(BytesCompleted);
GetCommand += "-\r\n\r\n";
Send(GetCommand, GetCommand.GetLength());
GnuComm->BytesOut += GetCommand.GetLength ();
}
// Incoming push
if(Type == 'U')
{
byte *ClientID = (byte *) &FileInfo.Guid;
std::basic_string<char> GiveMsg("GIV ");
GiveMsg += DWrdtoStr(FileInfo.Index) + ": ";
TCHAR buff[33];
sprintf(buff, "%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",
ClientID[0], ClientID[1], ClientID[2], ClientID[3], ClientID[4],
ClientID[5], ClientID[6], ClientID[7], ClientID[8], ClientID[9],
ClientID[10], ClientID[11], ClientID[12], ClientID[13], ClientID[14],
ClientID[15]);
GiveMsg.append(buff);
std::vector<SharedFile>::iterator it;
int loop;
for(loop = 0, it = GnuComm->Doc->SharedFiles.begin();
it != GnuComm->Doc->SharedFiles.end();
loop++, it++)
{
if(loop == FileInfo.Index)
FileInfo.FileName = (*it).FileName;
}
if(FileInfo.FileName == "")
return;
GiveMsg += "/" + FileInfo.FileName + "\n\n";
Send(GiveMsg.c_str(), GiveMsg.length());
}
}
else
Status = "Connection Refused";
CAsyncSocket::OnConnect(nErrorCode);
}
void CGnuTransfer::OnReceive(int nErrorCode)
{
DWORD dwSize = 0;
int iReceiveLength = 0;
int length = 0;
if (!IOCtl (FIONREAD, &dwSize))
{
// If we can't tell how many bytes are waiting on the socket, something's gone wrong.
Status = "Bad Socket";
Close ();
return;
}
byte *pBuff = new byte[dwSize+1];
iReceiveLength = Receive(pBuff, dwSize);
switch (iReceiveLength)
{
case 0:
// Connection has been closed, shutdown
WantToDisconnect();
// m_CanRelease = true;
// CleanUp();
break;
case SOCKET_ERROR:
OutputDebugString("CGnuSock::OnReceive - SOCKET_ERROR\n");
// received an error, see what it is and handle.
switch(GetLastError())
{
// fatal errors, need to disconnect
default:
case WSAECONNRESET: // The virtual circuit was reset by the remote side.
case WSAENOTCONN: // The socket is not connected.
case WSAENOTSOCK: // The descriptor is not a socket.
case WSAESHUTDOWN: // The socket has been shut down
case WSAECONNABORTED: // The virtual circuit was aborted due to timeout or other failure.
case WSAEWOULDBLOCK: //The socket is marked as nonblocking and the Receive operation would block. Shouldn't happen.
case WSAENETDOWN: // detected that the network subsystem failed
WantToDisconnect();
// m_CanRelease = true;
// CleanUp();
break;
// non fatal errors, just handle individualy
case WSAEMSGSIZE: // The datagram was too large to fit into the specified buffer and was truncated.
// Shouldn't happen because buffer is veriable sized
WantToDisconnect();
// m_CanRelease = true;
// CleanUp();
break;
}
delete [] pBuff;
return;
break;
default: // just normal data
m_timeLastHeardAT = CTime::GetCurrentTime();
length += iReceiveLength;
}
m_dwBytesIn += length;
GnuComm->BytesIn += length;
pBuff[length] = 0;
CString FileHeader = (char *) pBuff;
// If this is an upload, get out of here
if(FileHeader.Find( "GET /get/") != -1)
((CViewTransfer *) ((CGnucleusApp *) AfxGetApp())->TransferFrame->GetActiveView())->NewUpload(FileHeader, NULL);
else if(IsTransfering == 0)
{
// Be ready for response in the format of "HTTP/1.1 <Response-code>".
// Anything that doesn't start with HTTP should still be dropped
if(FileHeader.Find("HTTP 200 OK\r\nServer: ") != -1)
{
FileHeader.MakeLower();
// Extract client sending and file size from the header
int front = FileHeader.Find("server: ") + 8;
int back = FileHeader.Find("\r\n", front);
ClientName = FileHeader.Mid(front, back - front);
if(BytesCompleted)
{
front = FileHeader.Find("bytes=", back) + 6;
front = FileHeader.Find("-", front) + 1;
back = FileHeader.Find("/", front);
}
else
{
front = FileHeader.Find("content-length:") + 15;
back = FileHeader.Find("\r\n\r\n", front);
FileSize = atol( FileHeader.Mid(front, back - front));
}
IsTransfering = 1;
Status = "Downloading";
// Create the file and start initial d/l
if(BytesCompleted)
{
if(File.Open(GnuComm->Doc->m_DownloadDir + "\\" + (FileInfo.LocalFileName.IsEmpty() ? FileInfo.FileName : FileInfo.LocalFileName), CFile::modeCreate | CFile::modeNoTruncate | CFile::modeWrite | CFile::shareDenyNone))
IsFileOpen = 1;
File.SeekToEnd();
}
else
{
if (FileInfo.Size == 0)
{
FileInfo.Size = FileSize;
}
if(FileSize != FileInfo.Size)
{
Close();
Status = "File Invalid";
delete [] pBuff;
return;
}
if(File.Open(GnuComm->Doc->m_DownloadDir + "\\" + (FileInfo.LocalFileName.IsEmpty() ? FileInfo.FileName : FileInfo.LocalFileName), CFile::modeCreate | CFile::modeWrite | CFile::shareDenyNone))
IsFileOpen = 1;
}
int begin = FileHeader.Find("\r\n\r\n", front) + 4;
File.Write(&pBuff[begin], length - begin);
BytesCompleted += length - begin;
}
else
{
int httpError = 0;
::sscanf (FileHeader, "HTTP %d %*s", &httpError);
if (httpError != 0)
{
CString err;
Status.Format ("HTTP %d Error", httpError);
}
else
{
Status = "Bad Header";
}
Close();
}
}
else
{
if(BytesCompleted < FileSize)
{
File.Write(pBuff, length);
BytesCompleted += length;
}
else
{
IsTransfering = 0;
Close();
if(IsFileOpen)
File.Close();
IsFileOpen = false;
}
if(BytesCompleted == FileSize)
Status = "Completed";
}
m_timeLastHeardAT = CTime::GetCurrentTime();
delete [] pBuff;
CAsyncSocket::OnReceive(nErrorCode);
}
void CGnuTransfer::OnClose(int nErrorCode)
{
IsTransfering = 0;
if(IsFileOpen)
File.Close();
IsFileOpen = false;
// clean out the buffer
char * pBuf = new char[101];
int iRet = 0;
while( (iRet = Receive(pBuf, 100)) != 0 && iRet != SOCKET_ERROR );
delete [] pBuf;
if(BytesCompleted == FileSize)
Status = "Completed";
else
Status = "Remotely Canceled";
CAsyncSocket::OnClose(nErrorCode);
}
void CGnuTransfer::Pause()
{
IsTransfering = 0;
WantToDisconnect();
Close();
if(IsFileOpen)
File.Close();
IsFileOpen = false;
Status = "Paused";
}
void CGnuTransfer::Resume()
{
Status = "Active";
}
void CGnuTransfer::StartSending()
{
// Send the header
std::basic_string<char> Header("HTTP 200 OK\r\nServer: Gnutella\r\nContent-type:application/binary\r\n");
if(BytesCompleted)
Header += "Content-length: bytes=" + DWrdtoStr(BytesCompleted) + "-" + DWrdtoStr(FileSize - 1) + "/" + DWrdtoStr(FileSize) + "-\r\n\r\n";
else
Header += "Content-length:" + DWrdtoStr(FileSize) + "\r\n\r\n";
// Send the header
if(Header.length() != Send( Header.c_str(), Header.length()))
Close();
if( !File.Open(FileInfo.FileName, CFile::modeRead | CFile::shareDenyNone) )
return;
IsTransfering = 1;
IsFileOpen = 1;
Status = "Uploading";
File.Seek(BytesCompleted, CFile::begin);
m_threadHandle = (HANDLE) ::_beginthread (SendFile, 0, this);
}
void CGnuTransfer::WantToDisconnect()
{
// Let spock know that he is going to die, so it might as well
// speed up the process.
if(m_hSocket != INVALID_SOCKET)
{
AsyncSelect(FD_CLOSE);
ShutDown(sends);
}
IsTransfering = false;
}
void CGnuTransfer::SendFile(void *Link)
{
CGnuTransfer *GnuTrans = (CGnuTransfer *) Link;
CGnucleusDoc *Doc = GnuTrans->GnuComm->Doc;
int AllocBytes = 32000;
/*
if(Doc->m_LimitUp)
GnuTrans->AllocBw = Doc->m_LimitUp * 1024 / (1 + Doc->ActiveUploads);
else
GnuTrans->AllocBw = -1;
// Bandwidth management for uploads
if(GnuTrans->AllocBw != -1)
{
if(GnuTrans->AllocBw > 32000)
GnuTrans->AllocBw -= 32000;
else
{
AllocBytes = GnuTrans->AllocBw;
GnuTrans->AllocBw = 0;
}
}
*/
if(Doc->m_LimitUp)
GnuTrans->m_dwByteAllottmentOut = Doc->m_LimitUp * 1024 / 2; // preliminary allotment
else if(Doc->m_LimitTotal)
GnuTrans->m_dwByteAllottmentOut = Doc->m_LimitTotal * 1024 / 3;// preliminary allotment
if(GnuTrans->m_dwByteAllottmentOut)
{
if(GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut < 32000) // if there isn't 32k available, take what is.
{
AllocBytes = GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut;
}
}
BYTE *buffer = new BYTE[AllocBytes];
int bytesRead = 0;
if(GnuTrans->IsFileOpen)
bytesRead = GnuTrans->File.Read(buffer, AllocBytes);
while(GnuTrans->IsTransfering &&
GnuTrans->BytesCompleted != GnuTrans->FileSize &&
bytesRead > 0)
{
int bytesSent = 0;
if(GnuTrans->IsTransfering)
{
bytesSent = GnuTrans->Send(buffer + bytesSent, bytesRead - bytesSent);
GnuTrans->GnuComm->BytesOut += bytesSent;
GnuTrans->m_dwBytesOut += bytesSent;
}
while(GnuTrans->IsTransfering && bytesSent < bytesRead)
{
if(GetLastError() == WSAEWOULDBLOCK)
bytesSent += 1;
else if(GetLastError())
{
if(GnuTrans->IsTransfering)
GnuTrans->Status = "Error sending";
delete [] buffer;
return;
}
if(GnuTrans->IsTransfering)
{
bytesSent += GnuTrans->Send(buffer + bytesSent, bytesRead - bytesSent);
GnuTrans->GnuComm->BytesOut += bytesSent;
GnuTrans->m_dwBytesOut += bytesSent;
}
}
if(GnuTrans->IsTransfering)
{
GnuTrans->BytesCompleted += bytesSent;
GnuTrans->m_dwTotalBytesOut += bytesSent;
if(GnuTrans->BytesCompleted == GnuTrans->FileSize)
{
GnuTrans->Status = "Completed";
GnuTrans->IsTransfering = 0;
}
else
{
delete [] buffer;
AllocBytes = 32000;
// Bandwidth management for uploads
/*
if(GnuTrans->AllocBw != -1)
{
while(GnuTrans->IsTransfering && !GnuTrans->AllocBw);
if(GnuTrans->AllocBw > 32000)
GnuTrans->AllocBw -= 32000;
else
{
AllocBytes = GnuTrans->AllocBw;
GnuTrans->AllocBw = 0;
}
}
*/
if(GnuTrans->m_dwByteAllottmentOut)
{
while (GnuTrans->IsTransfering
&& (GnuTrans->m_dwBytesOut >= GnuTrans->m_dwByteAllottmentOut)
&& GnuTrans->m_dwByteAllottmentOut)
Sleep(50); // wait half a second
if(GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut < 32000) // if there isn't 32k available, take what is.
{
AllocBytes = GnuTrans->m_dwByteAllottmentOut - GnuTrans->m_dwBytesOut;
}
}
if(!AllocBytes)
AllocBytes = 32000;
buffer = new BYTE[AllocBytes];
if(GnuTrans->IsFileOpen)
bytesRead = GnuTrans->File.Read(buffer, AllocBytes);
}
}
}
delete [] buffer;
}